<?php
/**
 * Created by PhpStorm.
 * User: niqiaoyun
 * Date: 2017/9/1
 * Time: 22:19
 */

namespace app\common;

use common\widgets\Tools;
use Yii;
use yii\base\Model;
use yii\helpers\Json;
use yii\helpers\ArrayHelper;

class FormModel extends Model {

	private $_code = 0;

	private $_message = "success";

	private $_RESTfulResult;

	private $_form;

	/**
	 * 追踪列表
	 * @var array
	 */
	private static $_traces = [];
	/**
	 * 追踪上次的时间（毫秒）
	 * @var float
	 * 用于计算耗时
	 */
	private static $traceTime;
	/**
	 * 追踪上次的内存使用（byte）
	 * @var int
	 */
	private static $traceUsage;

	/**
	 * @param array $attributes
	 * @return mixed
	 */
	public static function instance($attributes = []) {
		static $instance;
		if(!isset($instance)) {
			$className = get_called_class();
			$instance = new $className($attributes);
		}
		return $instance;
	}

	public function setForm($form) {
		$this->_form = $form;
	}

	public function getForm() {
		$this->_form;
	}

	/**
	 * 默认规则
	 * @return array
	 */
	public function rules() {
		return [
			[$this->attributes(), 'safe'],
		];
	}

	/**
	 * 初始化参数Model
	 * @param array $data 属性数组
	 * @param string $className 类
	 * @param array $scenarios 校验场景
	 * @return mixed
	 */
	public function initParamModel(&$data, $className, $scenarios = null)
	{
		$dataModel = new $className();
		$dataModel->load($data, '');
		$dataModel->setForm($this->getForm() ? $this->getForm() : $this);
		if($scenarios) {
			if (is_string($scenarios)) {
				$scenarios = explode(',', $scenarios);
			}
		}else{
			$scenarios = [$this->scenario,];
		}
		if($dataModel->validateScenarios($scenarios) == false) {
			$this->addErrors($dataModel->getErrors());
			$data = null;
		}else{
			$data = $dataModel;
		}
		return $data;
	}

	/**
	 * 获取参数值
	 * @param $value 可传callback、@开头的字符串和mixed
	 * @return mixed
	 */
	private function getParamsValue($value) {
		if(is_callable($value)) {
			$value = $value();
		}elseif(is_string($value) && $value[0] == '@') {
			$value = substr($value, 1);
			$value = $this->{$value};
		}

		return $value;
	}

	/**
	 * 验证json
	 *
	 * @param string $attr
	 * @param array $params
	 */
	public function isJson($attr, $params) {
		if ($this->hasErrors () == false && isset($this->$attr))
		{
			$decode = Json::decode($this->$attr);
			if(empty($decode))
			{
				$this->addError($attr, \Yii::t('validator', 'Invalid Params', [$this->getAttributeLabel($attr)]));
			}
		}
	}

	/**
	 * 验证多场景
	 *
	 * @param array $scenarios
	 * @return boolean
	 */
	public function validateScenarios($scenarios) {
		foreach($scenarios as $scenario)
		{
			$this->setScenario($scenario);
			if($this->validate() == false)
			{
				return false;
			}
		}
		return TRUE;
	}

	/**
	 * 获取错误码
	 *
	 * @return mixed
	 */
	public function getCode()
	{
		return $this->_code;
	}

	/**
	 * 设置错误码
	 *
	 * @param mixed $code
	 */
	public function setCode($code) {
		$this->_code = $code;
	}

	/**
	 * 获取错误信息
	 *
	 * @return mixed
	 */
	public function getMessage() {
		return $this->_message;
	}

	/**
	 * 设置错误信息
	 *
	 * @param mixed $message
	 */
	public function setMessage($message) {
		$this->_message = $message;
	}

	/**
	 * 获取结果
	 *
	 * @return mixed
	 */
	public function getRESTfulResult() {
		return $this->_RESTfulResult;
	}

	/**
	 * 设置结果
	 *
	 * @param mixed $result
	 */
	public function setRESTfulResult($result) {
		$this->_RESTfulResult = $result;
	}

	/**
	 * (non-PHPdoc)
	 * @see \yii\base\Model::addError($attribute, $error)
	 */
	public function addError($attribute, $error = '') {
		if($this->_code == '0')
		{
			$this->_code = '100002';
			$this->_message = $error;
			$form = $this->getForm();
			if($form && $form != $this) {
				$form->addError($attribute, $error);
			}
		}
		parent::addError($attribute, $error);
	}

	/**
	 * 添加业务错误
	 * @param string $error
	 * @param mixed $result
	 * @return $this
	 */
	public function addServiceError($error, $result = null) {
		if(in_array($this->_code ,[0 ,200202 ,200204]))
		{
			if($this->_code == 0 ) {
				$this->_code = '200001';
			}
			$this->_message = "**".$error;
			$this->addError('service', $error);
			if(isset($result)) {
				$this->setRESTfulResult($result);
			}

			$form = $this->getForm();
			if($form && $form != $this) {
				$form->addServiceError($error, $result);
			}
		}
		return $this;
	}

	/**
	 * 批量添加错误信息
	 * @param $errors
	 */
	public function addErrors($errors) {
		if(empty($errors)) return;
		foreach($errors as $name => $es) {
			foreach($es as $e) {
				$this->addError($name, $e);
			}
		}
	}

	/**
	 * 设置错误信息
	 *
	 * @param string $code
	 * @param string $message
	 * @param mixed $result
	 * @return \common\FormModel
	 */
	public function setRESTfulError($code, $message, $result = NULL) {
		$this->_code = strval($code);
		$this->_message = strval($message);
		if(isset($result)) {
			$this->_RESTfulResult = $result;
		}

		// 联动修改
		$form = $this->getForm();
		if($form && $form != $this) {
			$form->setRESTfulError($code, $message, $result);
		}
		return $this;
	}

	/**
	 * RESTful输出
	 *
	 * @param mixed $result 内容
	 */
	public function RESTfulOut($result = NULL) {
		if(isset($result) == false) {
			$result = $this->_RESTfulResult;
		}

		$output = [
			'code' => $this->getCode(),
			'message' => $this->getMessage(),
			'data' => $result,
		];

		Tools::sendFormatJson($output);
	}

	public static function initTrace() {
		self::$traceTime = microtime(1);
		self::$traceUsage = memory_get_usage();
	}

	/**
	 * 添加追踪信息
	 * @param string $name 名称
	 * @param mixed $input 输入参数
	 * @param mixed $output 输出参数
	 */
	public static function addTrace($name, $input = null, $output = null) {
		// 耗时
		$time = microtime(1);
		$processedMicroSecond = (int)(($time-self::$traceTime)*1000);
		self::$traceTime = $time;

		// 内存
		$usage = memory_get_usage();
		$memoryUsage = floatval(bcsub($usage,self::$traceUsage,0));
		self::$traceUsage = $usage;

		array_unshift(self::$_traces,[
			'name' => $name,
			'input' => $input,
			'output' => $output,
			// 相对于上次trace的时间间隔（毫秒）
			'processedTime' => $processedMicroSecond.' ms',
			'memoryUsage' => Tools::formatBytes($memoryUsage),
		]);
	}

	/**
	 * 获取追踪列表
	 * @return array
	 */
	public static function getTraces() {
		return self::$_traces;
	}

	/**
	 * 记录日志
	 * @param string $class 类名称
	 * @param string $logDescription 该记录描述
	 * @param mixed $logData 日志明细
	 * @param string $logPath 日志文件名
	 * @return bool|json
	 * @author paul 16/9/7
	 */
	public function addLog($class, $logDescription, $logData, $logPath) {
		if (is_array ( $logData )) {
			$logData = json_encode ( $logData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES );
		}
		$logContent = '['.$class.']:' . $logDescription;
		if (false == empty ( $logData )) {
			$logContent .= '->' . $logData;
		}
		\Yii::info ( $logContent, $logPath );
		return;
	}
}